﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: FieldAttribute.cs
//
//  Description: Attribute used to map a member to a storage field.
// 
//--------------------------------------------------------------------------

using System;
using System.Reflection;

namespace Chili.Opf3
{
	/// <summary>This attribute is used to connect a property with a field in the storage.</summary>
	/// <remarks>
	/// The FieldAttribute is used to mark properties of the persistent class. Each
	/// property marked with this attribute corresponds to a field in the storage. The first
	/// argument that is passed to the attribute is the name of the field (in the entity) in
	/// the storage. After specifying the field optional properties may be set, which specify
	/// whether the field is an <see cref="Opf3.FieldAttribute.Identifier">identifier</see>,
	/// <see cref="Opf3.FieldAttribute.AllowDBNull">allows DBNull</see> or is an
	/// <see cref="Opf3.FieldAttribute.AutoNumber">auto number field</see> field. By specifying
	/// the property <see cref="Opf3.FieldAttribute.PersistBehaviours">PersistBehaviours</see> it
	/// is also possible to specify whether the property should be always saved or only if
	/// inserted or updated.
	/// </remarks>
	/// <example>
	///     The following example shows how to use the FieldAttribute in a persistent class.
	///     <code lang="CS">
	/// [Persistent("USER")]
	/// public class User
	/// {
	///     [Field("NAME", AllowDBNull = false)]
	///     public string Name
	///     {
	///         // ... code of the property.
	///     }
	///  
	///     [Field("ID", AllowDBNull = true, Identifier = true)]
	///     public string Id
	///     {
	///         // ... code of the property.
	///     }
	///  
	///     // ... Other code of the class.
	/// }
	///     </code>
	/// </example>
	[Serializable]
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
	public sealed class FieldAttribute : Attribute
	{
		private string _fieldName;
		private bool _allowDBNull = true;
		private bool _identifier;
		private bool _autoNumber;
		private PersistBehaviours _persistBehaviours = PersistBehaviours.None;
		private int _length = int.MaxValue;

		/// <summary>
		/// Creates a new instance of the <see cref="FieldAttribute">FieldAttribute
		/// Class</see>.
		/// </summary>
		public FieldAttribute()
		{
		}

		/// <summary>
		/// Creates a new instance of the <see cref="FieldAttribute">FieldAttribute
		/// Class</see>.
		/// </summary>
		/// <param name="fieldName">Name of the field in the storage that is mapped to the property.</param>
		/// <exception cref="ArgumentNullException">FieldName is null.</exception>
		public FieldAttribute(string fieldName)
		{
			if (fieldName == null)
				throw new ArgumentNullException("fieldName");
			_fieldName = fieldName;
		}

		/// <summary>
		/// Returns the name of the field in the storage that is mapped to the
		/// property.
		/// </summary>
		/// <value>
		/// A string that represents the field in the storage connected with the
		/// property.
		/// </value>
		public string FieldName
		{
			get { return _fieldName; }
			set 
			{
				if (value == null)
					throw new ArgumentNullException("value");
				
				_fieldName = value; 
			}
		}

		/// <summary>
		/// Specifies whether the field connected to the property is an identifier (primary
		/// key for some storage) of the entity.
		/// </summary>
		/// <value>Returns true if the field connected to the property is an identifier.</value>
		public bool Identifier
		{
			get { return _identifier; }
			set { _identifier = value; }
		}

		/// <summary>
		/// Specifies whether the field connected with the property is allowed to be DBNull
		/// in the storage.
		/// </summary>
		/// <remarks>
		/// The default behaviour is true, since usually properties are allowed to be DBNull.
		/// Set it to false to avoid saving of DBNull values (such properties are sometimes also
		/// named mandatory properties)
		/// </remarks>
		/// <value>Returns true if DBNull is allowed to be saved in the field.</value>
		public bool AllowDBNull
		{
			get { return _allowDBNull; }
			set { _allowDBNull = value; }
		}

		/// <summary>
		/// 	<para>Specifies whether the connected field of property is an auto number field in
		///     the storage.</para>
		/// </summary>
		/// <remarks>
		/// 	<para>Auto number fields are used by a storage to create unique keys for objects in
		///     the storage.</para>
		/// 	<para><b>Attention:</b> Currently auto numbers are only supported for
		///     <see cref="Opf3.Storages.OleDb.AccessStorage">AccessStorage</see> and
		///     <see cref="Opf3.Storages.MsSql.MsSqlStorage">MsSqlStorage</see>.</para>
		/// </remarks>
		/// <value>Returns true if the connected field is an auto number field.</value>
		public bool AutoNumber
		{
			get { return _autoNumber; }
			set { _autoNumber = value; }
		}

		/// <summary>Specifies the persist behaviour of the property.</summary>
		/// <remarks>
		/// 	<para>By setting the behaviour you may only allow to insert or update a property.
		///     The default behaviour is to insert and update the field in the storage with the
		///     value of the property.</para>
		/// </remarks>
		/// <value>Returns the persist behaviour of the property.</value>
		public PersistBehaviours PersistBehaviours
		{
			get { return _persistBehaviours; }
			set { _persistBehaviours = value; }
		}

		/// <summary>
		/// Gets and sets the maximum length for fields in the storage that hold strings.
		/// </summary>
		public int MaxLength
		{
			get { return _length; }
			set 
			{
				if (value < 0)
					throw new ArgumentException("Value is below zero. Not allowed.");
				
				_length = value; 
			}
		}
	}
}